﻿using System;

namespace MefBasic.Threading
{
    public class MultiThreadedJob:Job
    {
        public int TotalComplexity { get; set; }
        public int MaxChild { get; set; }
        public int ChildMinComplexity { get; set; }
        public Action<Job> ChildJobAction { get; private set; }
        public MultiThreadedJob(Action<Job> parentJobAction, Action<Job> childJobAction, Action<JobProgressArgs> progressNotifyAction) 
            : base(parentJobAction,progressNotifyAction)
        {
            ChildJobAction = childJobAction;
            BackgroundWorker=new Lazy<IBackgroundWorkerEx>();
            IsThreadSafe = true;
            MaxChild = Environment.ProcessorCount;
            ChildMinComplexity = 1;
        }

        private Lazy<IBackgroundWorkerEx> BackgroundWorker { get; set; }
        public override void Start()
        {
            if (InvokeStartingJob())
                return;
            WaitEvent.Reset();
            SplitAndStart();
        }

        private void SplitAndStart()
        {
            WaitForDependentJobs();
            int childCount;
            if (TotalComplexity > ChildMinComplexity)
            {
                childCount = TotalComplexity/ChildMinComplexity;
                if (TotalComplexity%ChildMinComplexity > 0)
                    childCount++;
                
                childCount = Math.Min(childCount, MaxChild);
            }
            else
            {
                childCount = 1;
            }
            var actualJobProgress = TotalComplexity / childCount;
            for (var i = 0; i < childCount; i++)
            {
                var start = i * actualJobProgress;
                var end = start + actualJobProgress;
                if (IsLastChildJob(childCount, i))
                {
                    end = TotalComplexity;
                }
                var child = new Job(ChildJobAction, ProgressNotifyAction)
                                {
                                    IsThreadSafe = IsThreadSafe,
                                    Id = String.Format("Child Job {0}", i),
                                    Store = { Start = start, End = end }
                                };
                AddChild(child);
            }
            DoStart();
        }

        private static bool IsLastChildJob(int childJobCount, int i)
        {
            return i == childJobCount - 1;
        }

        public void Block()
        {
            BackgroundWorker.Value.IsWorkerBusy = true;
            BackgroundWorker.Value.Block();
        }
    }
}